package com.quectel.core.module.device.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.quectel.core.module.application.service.ApplicationService;
import com.quectel.core.module.device.dao.DeviceDao;
import com.quectel.core.module.device.dto.DeviceCategoryDto;
import com.quectel.core.module.device.dto.DeviceDto;
import com.quectel.core.module.device.dto.DeviceTypeDto;
import com.quectel.core.module.device.dto.datav.DeviceCountDto;
import com.quectel.core.module.device.dto.datav.DeviceTypeCountDto;
import com.quectel.core.module.device.entity.DeviceEntity;
import com.quectel.core.module.device.service.DeviceCategoryService;
import com.quectel.core.module.device.service.DeviceService;
import com.quectel.core.module.device.service.DeviceTypeService;
import com.quectel.core.module.village.dto.VillageDto;
import com.quectel.core.module.village.dto.VillageRoomDto;
import com.quectel.core.module.village.service.VillageBuildingService;
import com.quectel.core.module.village.service.VillageFloorService;
import com.quectel.core.module.village.service.VillageRoomService;
import com.quectel.core.module.village.service.VillageService;
import com.quectel.util.common.CopyUtils;
import com.quectel.util.kit.CacheKit;
import com.quectel.util.kit.Snowflake;
import com.quectel.util.mybatis.MyBatisPlusUtils;
import com.quectel.util.redis.RedisUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.compress.utils.Lists;
import org.apache.dubbo.config.annotation.DubboService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.quectel.core.constants.RedisCacheConstants.PROJECT_NAME;

/**
 * @author rananxin
 * @date 2021-10-29 13:49:59
 */
@DubboService
public class DeviceServiceImpl extends ServiceImpl<DeviceDao, DeviceEntity> implements DeviceService {

    private static final Logger LOGGER = LoggerFactory.getLogger(DeviceServiceImpl.class);
    /**
     * 设备缓存 id
     */
    public static final String LAST_DEVICE_DTO_BY_ID = PROJECT_NAME + "LAST_DEVICE_DTO_BY_ID:";

    /**
     * 设备缓存 imei
     */
    public static final String LAST_DEVICE_DTO_BY_IMEI = PROJECT_NAME + "LAST_DEVICE_DTO_BY_IMEI:";
    /**
     * 设备缓存 uniqueId
     */
    public static final String LAST_DEVICE_DTO_BY_UNIQUE_ID = PROJECT_NAME + "LAST_DEVICE_DTO_BY_UNIQUE_ID:";

    @Autowired
    private DeviceDao deviceDao;
    @Autowired
    private DeviceCategoryService deviceCategoryService;
    @Autowired
    private DeviceTypeService deviceTypeService;
    @Autowired
    private VillageService villageService;
    @Autowired
    private VillageBuildingService villageBuildingService;
    @Autowired
    private VillageFloorService villageFloorService;
    @Autowired
    private VillageRoomService villageRoomService;
    @Autowired
    private ApplicationService applicationService;


    @Override
    public DeviceDto selectByIdWithoutPadding(Long deviceId) {
        DeviceEntity entity = deviceDao.selectById(deviceId);
        DeviceDto deviceDto = CopyUtils.copyObj(entity, DeviceDto.class);
        return deviceDto;
    }

    @Override
    public List<DeviceDto> queryListLessPadding(Map<String, Object> params) {
        List<DeviceEntity> list = deviceDao.queryList(params);
        List<DeviceDto> deviceDtos = CopyUtils.copyList(list, DeviceDto.class);
        deviceDtos.forEach(this::empPaddingField);
        return deviceDtos;
    }

    private void empPaddingField(DeviceDto deviceDto) {
        if (deviceDto == null) {
            return;
        }

        DeviceCategoryDto deviceCategoryDto = deviceCategoryService.selectCacheById(deviceDto.getDeviceCategoryId());
        deviceDto.setDeviceCategory(deviceCategoryDto);

        DeviceTypeDto deviceTypeDto = deviceTypeService.selectCacheById(deviceDto.getDeviceTypeId());
        deviceDto.setDeviceTypeName(deviceTypeDto.getName());
    }

    @Override
    public List<DeviceDto> queryByParent(Long deviceId) {
        List<DeviceEntity> deviceEntities = deviceDao.queryByParent(deviceId);
        List<DeviceDto> deviceDtos = CopyUtils.copyList(deviceEntities, DeviceDto.class);
        deviceDtos.forEach(this::paddingField);
        return deviceDtos;
    }

    @Override
    public int queryTreeTotal(Map<String, Object> params) {
        return deviceDao.queryTreeTotal(params);
    }

    @Override
    public List<DeviceDto> queryTreeList(Map<String, Object> params) {
        List<DeviceEntity> allList = Lists.newArrayList();

        List<DeviceEntity> list = deviceDao.queryTreeRootList(params);
        for (DeviceEntity deviceEntity : list) {
            List<DeviceEntity> child = deviceDao.queryByParent(deviceEntity.getId());
            if (CollectionUtils.isNotEmpty(child)) {
                allList.addAll(child);
            }
        }

        allList.addAll(list);
        List<DeviceDto> deviceDtos = CopyUtils.copyList(allList, DeviceDto.class);

        deviceDtos.forEach(this::paddingField);

        return deviceDtos;
    }

    @Override
    public List<DeviceDto> selectByApplicationId(Long applicationId) {
        List<DeviceEntity> deviceEntities = deviceDao.selectList(new LambdaQueryWrapper<DeviceEntity>()
                .eq(DeviceEntity::getApplicationId, applicationId));
        List<DeviceDto> deviceDtos = CopyUtils.copyList(deviceEntities, DeviceDto.class);
        deviceDtos.forEach(this::paddingField);
        return deviceDtos;
    }

    @Override
    public void saveBatch(List<DeviceDto> dtos) {
        List<DeviceEntity> deviceEntities = CopyUtils.copyList(dtos, DeviceEntity.class);
        for (DeviceEntity deviceEntity : deviceEntities) {
            deviceEntity.setId(Snowflake.nextId());
        }
        super.saveBatch(deviceEntities);
    }

    @Override
    public List<DeviceDto> queryAtlasBindDevices(Long roomId) {
        List<DeviceDto> result = new ArrayList<>();
        VillageRoomDto villageRoomDto = villageRoomService.selectById(roomId);
        if (villageRoomDto != null) {
            Map<String, Object> params = new HashMap<String, Object>();
            params.put("villageId", villageRoomDto.getVillageId());
            params.put("buildingId", villageRoomDto.getBuildingId());
            params.put("floorId", villageRoomDto.getFloorId());
            params.put("roomId", villageRoomDto.getId());
            List<DeviceEntity> atlasBindDevices = deviceDao.queryAtlasBindDevices(params);
            result = CopyUtils.copyList(atlasBindDevices, DeviceDto.class);
        }
        return result;
    }

    @Override
    public List<DeviceTypeCountDto> countDeviceType(Map<String, Object> params) {
        List<DeviceTypeCountDto> list = deviceDao.countDeviceType(params);
        for (DeviceTypeCountDto deviceTypeCountDto : list) {
            if (deviceTypeCountDto.getDeviceTypeId() != null) {
                DeviceTypeDto deviceTypeDto = deviceTypeService.selectCacheById(deviceTypeCountDto.getDeviceTypeId());
                if (deviceTypeDto != null) {
                    deviceTypeCountDto.setDeviceTypeName(deviceTypeDto.getName());
                    deviceTypeCountDto.setDeviceTypeCode(deviceTypeDto.getCode());

                    if (deviceTypeDto.getDeviceCategoryId() != null) {
                        DeviceCategoryDto deviceCategoryDto = deviceCategoryService.selectCacheById(deviceTypeDto.getDeviceCategoryId());
                        if (deviceCategoryDto != null) {
                            deviceTypeCountDto.setDeviceCategoryId(deviceCategoryDto.getId());
                            deviceTypeCountDto.setDeviceCategoryCode(deviceCategoryDto.getCode());
                            deviceTypeCountDto.setDeviceCategoryName(deviceCategoryDto.getName());
                            deviceTypeCountDto.setDeviceCategoryIconJson(deviceCategoryDto.getIconJson());
                        }
                    }
                }
            }
        }
        return list;
    }

    @Override
    public List<DeviceDto> selectCacheByImei(String imei) {
        List<DeviceDto> deviceDtos = CacheKit.cacheToRedis(
                () -> {
                    List<DeviceEntity> deviceEntitys = deviceDao.selectList(new LambdaQueryWrapper<DeviceEntity>()
                            .eq(DeviceEntity::getImei, imei));
                    List<DeviceDto> dtos = CopyUtils.copyList(deviceEntitys, DeviceDto.class);
                    dtos.forEach(o -> paddingField(o));
                    return dtos;
                },
                LAST_DEVICE_DTO_BY_IMEI + imei,

                5 * 60
        );
        return deviceDtos;
    }

    @Override
    public List<DeviceDto> selectCacheByUniqueId(String uniqueId) {
        List<DeviceDto> deviceDtos = CacheKit.cacheToRedis(
                () -> {
                    List<DeviceEntity> deviceEntitys = deviceDao.selectList(new LambdaQueryWrapper<DeviceEntity>()
                            .eq(DeviceEntity::getUniqueId, uniqueId));
                    List<DeviceDto> dtos = CopyUtils.copyList(deviceEntitys, DeviceDto.class);
                    dtos.forEach(o -> paddingField(o));
                    return dtos;
                },
                LAST_DEVICE_DTO_BY_UNIQUE_ID + uniqueId,

                5 * 60
        );
        return deviceDtos;
    }

    @Override
    public DeviceCountDto countDevice(Map<String, Object> params) {
        return deviceDao.countDevice(params);
    }

    @Override
    public DeviceDto selectCacheByImei(String imei, Long tenantId) {
        DeviceDto deviceDto = CacheKit.cacheToRedis(
                () -> {
                    DeviceEntity deviceEntity = deviceDao.selectOne(new LambdaQueryWrapper<DeviceEntity>()
                            .eq(DeviceEntity::getImei, imei)
                            .eq(DeviceEntity::getTenantId, tenantId));
                    DeviceDto dto = CopyUtils.copyObj(deviceEntity, DeviceDto.class);
                    paddingField(dto);
                    return dto;
                },
                LAST_DEVICE_DTO_BY_IMEI + imei + ":" + tenantId,

                5 * 60
        );
        return deviceDto;
    }

    @Override
    public DeviceDto selectCacheByUniqueId(String uniqueId, Long tenantId) {
        DeviceDto deviceDto = CacheKit.cacheToRedis(
                () -> selectByUniqueId(uniqueId, tenantId),
                LAST_DEVICE_DTO_BY_UNIQUE_ID + uniqueId + ":" + tenantId,

                5 * 60
        );
        return deviceDto;

    }

    @Override
    public DeviceDto selectByUniqueId(String uniqueId, Long tenantId) {
        DeviceEntity deviceEntity = deviceDao.selectOne(new LambdaQueryWrapper<DeviceEntity>()
                .eq(DeviceEntity::getUniqueId, uniqueId)
                .eq(DeviceEntity::getTenantId, tenantId));
        DeviceDto dto = CopyUtils.copyObj(deviceEntity, DeviceDto.class);
        paddingField(dto);
        return dto;
    }

    @Override
    public DeviceDto selectCacheById(Long id) {
        return CacheKit.cacheToRedis(
                () -> selectById(id),
                LAST_DEVICE_DTO_BY_ID + id,

                5 * 60
        );
    }

    @Override
    public DeviceDto selectById(Long id) {
        DeviceEntity entity = deviceDao.selectById(id);
        DeviceDto deviceDto = CopyUtils.copyObj(entity, DeviceDto.class);
        paddingField(deviceDto);
        return deviceDto;
    }

    private void paddingField(DeviceDto deviceDto) {
        if (deviceDto == null) {
            return;
        }

        DeviceCategoryDto deviceCategoryDto = deviceCategoryService.selectCacheById(deviceDto.getDeviceCategoryId());
        deviceDto.setDeviceCategory(deviceCategoryDto);

        DeviceTypeDto deviceTypeDto = deviceTypeService.selectCacheById(deviceDto.getDeviceTypeId());
        deviceDto.setDeviceType(deviceTypeDto);

        VillageDto villageDto = villageService.selectCacheById(deviceDto.getVillageId());
        deviceDto.setVillage(villageDto);

        if (deviceDto.getBuildingId() != null) {
            deviceDto.setBuilding(villageBuildingService.selectCacheById(deviceDto.getBuildingId()));
        }
        if (deviceDto.getFloorId() != null) {
            deviceDto.setFloor(villageFloorService.selectCacheById(deviceDto.getFloorId()));
        }
        if (deviceDto.getRoomId() != null) {
            deviceDto.setRoom(villageRoomService.selectCacheById(deviceDto.getRoomId()));
        }
        if (deviceDto.getApplicationId() != null) {
            deviceDto.setApplication(applicationService.selectCacheById(deviceDto.getApplicationId()));
        }
    }


    @Override
    public List<DeviceDto> queryList(Map<String, Object> params) {
        List<DeviceEntity> list = deviceDao.queryList(params);
        List<DeviceDto> deviceDtos = CopyUtils.copyList(list, DeviceDto.class);

        deviceDtos.forEach(this::paddingField);

        return deviceDtos;
    }

    @Override
    public int queryTotal(Map<String, Object> params) {
        return deviceDao.queryTotal(params);
    }

    @Override
    public Long save(DeviceDto dto) {
        DeviceEntity entity = CopyUtils.copyObj(dto, DeviceEntity.class);
        entity.setId(Snowflake.nextId());
        deviceDao.insert(entity);
        return entity.getId();
    }

    @Override
    public void updateById(DeviceDto dto) {
        DeviceEntity entity = CopyUtils.copyObj(dto, DeviceEntity.class);
        deviceDao.updateById(entity);
        delCacheDevice(LAST_DEVICE_DTO_BY_ID + dto.getId()
                , LAST_DEVICE_DTO_BY_IMEI + dto.getImei()
                , LAST_DEVICE_DTO_BY_UNIQUE_ID + dto.getUniqueId());
    }

    @Override
    public void updateAllColumnById(DeviceDto dto) {

        DeviceEntity entity = CopyUtils.copyObj(dto, DeviceEntity.class);


        MyBatisPlusUtils.updateAllColumnById(entity, deviceDao);

        delCacheDevice(LAST_DEVICE_DTO_BY_ID + dto.getId()
                , LAST_DEVICE_DTO_BY_IMEI + dto.getImei()
                , LAST_DEVICE_DTO_BY_UNIQUE_ID + dto.getUniqueId());
    }

    @Override
    public void deleteById(Long id) {
        DeviceEntity deviceEntity = deviceDao.selectById(id);
        if (deviceEntity != null) {
            deviceDao.deleteById(id);
            delCacheDevice(LAST_DEVICE_DTO_BY_ID + id
                    , LAST_DEVICE_DTO_BY_IMEI + deviceEntity.getImei()
                    , LAST_DEVICE_DTO_BY_UNIQUE_ID + deviceEntity.getUniqueId());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteBatch(Long[] ids) {
        for (Long id : ids) {
            deleteById(id);
        }
    }

    private void delCacheDevice(String... keys) {
        for (String key : keys) {
            RedisUtils.del(key);
        }
    }

}
